package com.zhss.demo.register.server;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * 服务注册表
 */
public class ServiceRegistry {

    public static final Long RECENTLY_CHANGED_ITEM_CHECK_INTERVAL = 3000L;
    public static final Long RECENTLY_CHANGED_ITEM_EXPIRED = 3 * 60 * 1000L;
    /**
     * 核心的内存数据结构：注册表
     * Map：key是服务名称，value是这个服务的所有的服务实例
     * Map<String, ServiceInstance>：key是服务实例id，value是服务实例的信息
     */
    private Map<String, Map<String, ServiceInstance>> registry = new ConcurrentHashMap<String, Map<String, ServiceInstance>>();
    /**
     * 最近变更的服务实例的队列
     */
    private Queue<RecentlyChangedServiceInstance> recentlyChangedQueue = new ConcurrentLinkedQueue<>();
    /**
     * 服务注册表的锁
     */
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    /**
     * 读锁
     */
    private ReadLock readLock = lock.readLock();
    /**
     * 写锁
     */
    private WriteLock writeLock = lock.writeLock();
    /**
     * 注册表是一个单例
     */
    private static ServiceRegistry instance = new ServiceRegistry();

    private ServiceRegistry() {
        // 启动后台线程监控最近变更的队列
        RecentlyChangedQueueMonitor recentlyChangedQueueMonitor = new RecentlyChangedQueueMonitor();
        recentlyChangedQueueMonitor.setDaemon(true);
        recentlyChangedQueueMonitor.start();
    }

    /**
     * 获取服务注册中心注册表
     * @return
     */
    public static ServiceRegistry getInstance() {
        return instance;
    }

    /**
     * 加读锁
     */
    public void readLock() {
        this.readLock.lock();
    }

    /**
     * 释放读锁
     */
    public void unReadLock() {
        this.readLock.unlock();
    }

    /**
     * 加写锁
     */
    public void writeLock() {
        this.writeLock.lock();
    }

    /**
     * 释放写锁
     */
    public void unWriteLock() {
        this.writeLock.unlock();
    }

    /**
     * 服务注册
     * @param serviceInstance 服务实例
     */
    public /*synchronized*/ void register(ServiceInstance serviceInstance) {
        try {
            // 加入写锁
            this.writeLock();
            // 将服务实例放入最近变更的队列中
            RecentlyChangedServiceInstance recentlyChangedItem = new RecentlyChangedServiceInstance(
                    serviceInstance,
                    System.currentTimeMillis(),
                    ServiceInstanceOperation.REGISTER);
            recentlyChangedQueue.offer(recentlyChangedItem);

            /**
             * 根据服务名称到注册表中获取该服务的注册信息,此处使用相当于一下这段代码
             * 	Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceInstance.getServiceName());
             * 		if(serviceInstanceMap == null) {
             * 			serviceInstanceMap = new HashMap<String, ServiceInstance>();
             * 			registry.put(serviceInstance.getServiceName(), serviceInstanceMap);
             *                }
             * 这里表达式的意思就是：从注册表中根据服务名称来获取服务MAP，如果没有获取到就新建一个服务MAP
             */
            Map<String, ServiceInstance> serviceInstanceMap =
                    registry.computeIfAbsent(serviceInstance.getServiceName(), k -> new HashMap<String, ServiceInstance>());

            // 服务MAP键为serviceInstanceId 值为整个服务实例
            serviceInstanceMap.put(serviceInstance.getServiceInstanceId(), serviceInstance);

            System.out.println("服务实例【" + serviceInstance + "】，完成注册......");
            System.out.println("注册表：" + registry);
        } finally {
            this.unWriteLock();
        }
    }


    /**
     * 获取服务实例
     * @param serviceName       服务名称
     * @param serviceInstanceId 服务实例id
     * @return 服务实例
     */
    public /*synchronized*/ ServiceInstance getServiceInstance(String serviceName, String serviceInstanceId) {
/*        try {
            this.readLock();
            Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceName);
            return serviceInstanceMap.get(serviceInstanceId);
        } finally {
            this.unReadLock();
        }*/
        Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceName);
        return serviceInstanceMap.get(serviceInstanceId);
    }

    /**
     * 获取整个注册表
     */
    public /*synchronized*/ Map<String, Map<String, ServiceInstance>> getRegistry() {
        return registry;
    }

    /**
     * 获取最近有变化的注册表
     * @return
     */
    public /*synchronized*/ DeltaRegistry getDeltaRegistry() {
        Long totalCount = 0L;
        for (Map<String, ServiceInstance> serviceInstanceMap : registry.values()) {
            totalCount += serviceInstanceMap.size();
        }
        DeltaRegistry deltaRegistry = new DeltaRegistry(recentlyChangedQueue, totalCount);
        return deltaRegistry;
    }

    /**
     * 从注册表删除一个服务实例
     * @param serviceName
     * @param serviceInstanceId
     */
    public /*synchronized*/ void remove(String serviceName, String serviceInstanceId) {
        try {
            // 加写锁
            this.writeLock();
            System.out.println("服务实例【" + serviceInstanceId + "】，从注册表中进行摘除");
            Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceName);
            ServiceInstance serviceInstance = serviceInstanceMap.get(serviceInstanceId);

            // 将服务实例变更信息放入队列中
            RecentlyChangedServiceInstance recentlyChangedItem = new RecentlyChangedServiceInstance(
                    serviceInstance,
                    System.currentTimeMillis(),
                    ServiceInstanceOperation.REMOVE);
            recentlyChangedQueue.offer(recentlyChangedItem);

            serviceInstanceMap.remove(serviceInstanceId);
        } finally {
            this.unWriteLock();
        }
    }


    /**
     * 最近变化的服务实例
     * @author zhonghuashishan
     */
    class RecentlyChangedServiceInstance {

        /**
         * 服务实例
         */
        ServiceInstance serviceInstance;
        /**
         * 发生变更的时间戳
         */
        Long changedTimestamp;
        /**
         * 变更操作
         */
        String serviceInstanceOperation;

        public RecentlyChangedServiceInstance(
                ServiceInstance serviceInstance,
                Long changedTimestamp,
                String serviceInstanceOperation) {
            this.serviceInstance = serviceInstance;
            this.changedTimestamp = changedTimestamp;
            this.serviceInstanceOperation = serviceInstanceOperation;
        }

    }

    /**
     * 服务实例操作
     * @author zhonghuashishan
     */
    class ServiceInstanceOperation {

        /**
         * 注册
         */
        public static final String REGISTER = "register";
        /**
         * 删除
         */
        public static final String REMOVE = "REMOVE";

    }

    /**
     * 最近变更队列的监控线程
     * @author zhonghuashishan
     */
    class RecentlyChangedQueueMonitor extends Thread {

        @Override
        public void run() {
            while (true) {
                try {
                    // 每3秒钟去检测变更服务队列中的服务实例是否已经超过三分钟，如果超过则从变更队列中删除该实例，该实例就不是增量实例
       /*             synchronized (instance) {

                    }*/
                    writeLock();
                    // 获取增量队列中注册和移除事件
                    RecentlyChangedServiceInstance recentlyChangedItem = recentlyChangedQueue.peek();
                    Long currentTimestamp = System.currentTimeMillis();
                    while (recentlyChangedItem != null) {
                        // 判断如果一个服务实例变更信息已经在队列里存在超过3分钟了 就从队列中移除
                        if (currentTimestamp - recentlyChangedItem.changedTimestamp > RECENTLY_CHANGED_ITEM_EXPIRED) {
                            recentlyChangedQueue.poll();
                        }
                    }
                    Thread.sleep(RECENTLY_CHANGED_ITEM_CHECK_INTERVAL);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    unWriteLock();
                }
            }
        }

    }

}
